package com.owc.service.impl.app;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.owc.common.enums.CycleType;
import com.owc.common.enums.ExceptionEnum;
import com.owc.common.enums.FlowTypeEnum;
import com.owc.common.enums.OrderType;
import com.owc.common.exception.BadRequestException;
import com.owc.common.exception.OWCException;
import com.owc.common.exception.ParamsException;
import com.owc.common.lock.Lock;
import com.owc.common.lock.ULock;
import com.owc.common.tool.BeanUtils;
import com.owc.common.tool.DateUtils;
import com.owc.common.tool.IDGenerater;
import com.owc.dao.*;
import com.owc.domain.DO.BuyModel;
import com.owc.domain.PageBean;
import com.owc.domain.dto.OrderDTO;
import com.owc.domain.dto.OrderDetailDTO;
import com.owc.domain.dto.OrderYesterdayIncomeDTO;
import com.owc.domain.entity.Order;
import com.owc.domain.entity.Product;
import com.owc.domain.entity.TokenBalance;
import com.owc.domain.entity.User;
import com.owc.domain.vo.admin.WealthQueryVo;
import com.owc.filter.content.SecurityContextHolder;
import com.owc.service.admin.AccountFlowService;
import com.owc.service.app.OrderService;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author xiaom
 * @Date 2020/2/26 16:38
 * @Version 1.0.0
 * @Description <>
 **/
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private ProductMapper productMapper;
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private TokenBalanceMapper tokenBalanceMapper;
    @Resource
    private IncomeRecordMapper incomeRecordMapper;
    @Resource
    private UserMapper userMapper;

    @Autowired
    private IDGenerater idGenerater;
    @Autowired
    private AccountFlowService accountFlowService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    @ULock(lockKey = "#{buyModel.userId}")
    public Long generateOrder(BuyModel buyModel) throws Exception {
        Assert.notNull(buyModel.getUserId(),"Buy product userId must not be null");
        try {
            //获取用户详细信息
            User user = userMapper.selectByPrimaryKey(buyModel.getUserId());
            if(user == null){
                throw new Exception("Not-exist userId:[" + buyModel.getUserId() + "]");
            }
            //获取产品详细信息
            Product product = productMapper.selectByPrimaryKey(buyModel.getProductId());
            Order order = loadOrderModel(product,user);
            order.setAmount(buyModel.getBuyAmount());
            order.setProxyUserId(buyModel.getProxyUserId());
            order.setChannel(buyModel.getChannel().getType());

            //生成购买订单
            orderMapper.insert(order);
            //减少用户可用余额/增加冻结余额
            tokenBalanceMapper.updateBalance(buyModel.getUserId(),product.getTokenId(),buyModel.getBuyAmount());
            //生成流水信息
            accountFlowService.insertFlow(FlowTypeEnum.lock,buyModel.getUserId(),buyModel.getBuyAmount(),"",product.getTokenId(),order.getOrderId() + "",null);
            log.info("购买产品成功. 购买用户:[{}] 购买金额[{}] 产品名称:[{}]",user.getId(),buyModel.getBuyAmount(),product.getName());
            return order.getOrderId();
        }catch (Exception e){
            log.error("Buy product throw error. {}",e.getMessage());
            throw new Exception();
        }
    }

    @Override
    public PageBean<OrderDTO> getOrderList(Integer pageIndex, Integer pageSize) {

        PageHelper.startPage(pageIndex,pageSize);
        List<Order> orderList = orderMapper.selectByUserId(SecurityContextHolder.getCurrentUserId());
        PageInfo<Order> pageInfo = new PageInfo<>(orderList);

        if(pageInfo.getList().size() == 0){
            return PageBean.pageBean(new ArrayList<>());
        }

        List<OrderDTO> orderDTOList = BeanUtils.coventObject(orderList,OrderDTO.class);
        //获取昨日收益
        List<OrderYesterdayIncomeDTO> yesterdayIncomeList = incomeRecordMapper.selectYesterdayInOrderIds(orderList
                .parallelStream()
                .map(Order::getOrderId)
                .collect(Collectors.toList()));

        Map<Long,BigDecimal> yesterdayIncomeMap = yesterdayIncomeList
                .parallelStream()
                .collect(Collectors.toMap(OrderYesterdayIncomeDTO::getOrderId,OrderYesterdayIncomeDTO::getIncome));

        orderDTOList.forEach(item -> {
            if(yesterdayIncomeMap.containsKey(item.getOrderId())){
                item.setYesterdayIncome(yesterdayIncomeMap.get(item.getOrderId()));
            }
        });
        return PageBean.pageBean(pageInfo.getTotal(), orderDTOList);
    }

    @Override
    public OrderDetailDTO getOrderDetail(Long id) {
        OrderDetailDTO detailDTO = orderMapper.selectOrderDetailById(id,SecurityContextHolder.getCurrentUserId());
        if(detailDTO == null){
            return null;
        }
        //计算剩余赎回天数
        if(OrderType.VALID_TYPE.getType().equals(detailDTO.getStatus())){
            long day;
            if(CycleType.YEAR_TYPE.getType().equals(detailDTO.getCycleType())){
                //计算天数
                day = detailDTO.getCycle() * DateUtils.TIME_ONE_YEAR;
            }else{
                day = detailDTO.getCycle();
            }
            //计算已经购买天数
            long interval = DateUtils.differentDaysByDay(DateUtils.parseDate(detailDTO.getBuyTime(),DateUtils.DATE_FORMAT_YYYY_MM_DD_HH_MM_SS),new Date());
            detailDTO.setCanRecycle(day - interval + 1);
        }
        return detailDTO;
    }

    @Override
    public PageInfo<Order> getOrderListByProductId(Long productId,Integer pageIndex,Integer pageSize) {
        PageHelper.startPage(pageIndex,pageSize);
        List<Order> list = orderMapper.selectOrdersByProductId(productId);
        return new PageInfo<>(list);
    }

    @Override
    public BigDecimal statisticsAmount(Long id) {
        return orderMapper.statisticsAmount(id);
    }

    @Override
    public PageInfo<Order> getAllOrderList(WealthQueryVo wealthQueryVo) {
        PageHelper.startPage(wealthQueryVo.getPageIndex(),wealthQueryVo.getPageSize());
        List<Order> list = orderMapper.selectAllByFilter(wealthQueryVo);
        return new PageInfo<>(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean redemption(Long id) throws Exception {

        Order order = orderMapper.selectByOrderId(id,SecurityContextHolder.getCurrentUserId());
        Assert.notNull(order,ExceptionEnum.NOT_EXIST_ORDER.getMessage());

        if(OrderType.FINISH_TYPE.getType().equals(order.getStatus())){
            throw new BadRequestException(ExceptionEnum.ORDER_ALREADY_REDEEM);
        }

        //获取购买(赎回)金额
        BigDecimal amount = order.getAmount();
        //获取用户冻结余额
        TokenBalance tokenBalance = tokenBalanceMapper.selectByUserIdAndTokenId(SecurityContextHolder.getCurrentUserId(),order.getTokenId());
        Assert.notNull(tokenBalance,ExceptionEnum.INVALID_TOKEN_ERR.getMessage());
        if(tokenBalance.getFreeze().compareTo(amount) < 0){
            log.error("异常的赎回现象发生,因为赎回金额:[{}] 大于账户冻结余额:[{}]",amount,tokenBalance.getFreeze());
            throw new BadRequestException(ExceptionEnum.BAD_REQUEST_ERR);
        }
        //增加用户余额
        tokenBalanceMapper.updateBalance(SecurityContextHolder.getCurrentUserId(),order.getTokenId(),amount.multiply(new BigDecimal(-1)));
        //修改订单状态
        orderMapper.updateStatusByOrderId(OrderType.FINISH_TYPE.getType(),order.getOrderId(), SecurityContextHolder.getCurrentUserId(),DateUtils.formatYMDHMS(new Date()));
        //生成流水
        accountFlowService.insertFlow(FlowTypeEnum.redemption,SecurityContextHolder.getCurrentUserId(),amount,"",order.getTokenId(),order.getOrderId() + "",null);
        return true;
    }

    @Override
    public List<Order> getValidOrderList(OrderType type) {
        if(type == null){
            type = OrderType.VALID_TYPE;
        }
        return orderMapper.selectByStatus(type.getType());
    }

    private Order loadOrderModel(Product product, User user) {
        Order order = new Order();
        order.setBuyTime(DateUtils.formatYMDHMS(new Date()));
        //计算结束时间
        order.setExpireTime(calculationExpireTime(product));
        order.setBuyUserId(user.getId());
        order.setBuyUserName(user.getUsername());
        order.setPhone(user.getPhone());
        order.setProductId(product.getId());
        order.setProductName(product.getName());
        order.setStatus(OrderType.VALID_TYPE.getType());
        order.setTokenId(product.getTokenId());
        order.setTokenName(product.getTokenName());
        order.setOrderId(idGenerater.nextId());
        order.setIncomeId(product.getIncomeId());
        order.setIncomeName(product.getIncomeName());
        return order;
    }

    private String calculationExpireTime(Product product) {

        Optional<CycleType> optionalCycleType = CycleType.match(product.getCycleType());
        if(!optionalCycleType.isPresent()){
            throw new ParamsException(ExceptionEnum.INVALID_PRODUCT_ERR);
        }

        long afterDays = 0L;
        switch (optionalCycleType.get()){
            case YEAR_TYPE:
                afterDays = product.getCycle() * DateUtils.TIME_ONE_YEAR;
                break;
            case DAY_TYPE:
                afterDays = product.getCycle();
                break;
            default:
        }
        return DateUtils.getAfterHHMMSSDays(new Date(), (int) afterDays);
    }
}
